/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.osgi.framework.BundleContext;

import com.cea.papyrus.core.editor.CreationEntryFactory;
import com.cea.papyrus.core.editor.MultiDiagramEditor;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.helper.ModelerHelperExtensionRegistry;
import com.cea.papyrus.core.helper.ModelerHelperRegistry;
import com.cea.papyrus.core.utils.ColorManager;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.FontManager;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.core.utils.UMLObjectLabelProvider;
import com.cea.papyrus.core.utils.UMLObjectShortLabelProvider;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;


/**
 * The main plugin class to be used in the desktop.
 */
public class PapyrusPlugin extends AbstractUIPlugin implements IPapyrusUIConstants {

	/** share instance of the plugin. */
	private static PapyrusPlugin plugin;
	
	private Package uml2Metamodel;

	/** Plugin unique identifier. */
	public static final String PLUGIN_ID = "com.cea.papyrus.core";

	/** Label provider for tabbed properties. */
	public static UMLObjectLabelProvider UML_LABEL_PROVIDER;

	/** Label provider for tabbed properties. */
	public static UMLObjectShortLabelProvider UML_SHORT_LABEL_PROVIDER;

	/** Path for icons. */	
	public static final String UML_ICONS_16x16		= "icons/uml/obj16/";

	/** The Constant UML_VIS_ICONS_16x16. */
	public static final String UML_VIS_ICONS_16x16	= "icons/uml/ovr16/";

	/** Path for icons16x16. */
	public static final String PAPYRUS_ICONS_16x16	= "icons/papyrus/16x16/";

	/** Path for icons32x32. */
	public static final String PAPYRUS_ICONS_32x32	= "icons/papyrus/32x32/";

	/** Path for others figures. */
	public static final String PAPYRUS_FIGURES	    = "icons/papyrus/figures/";

	/** Default image. */
	public static final String DEFAULT_IMAGE = "icons/papyrus/Papyrus.gif";

	/** left Stereotype delimiters ('Guillemets francais'). */
	public static  String ST_LEFT	= String.valueOf("\u00AB"); 

	/** Right Stereotype delimiters ('Guillemets francais'). */
	public static  String ST_RIGHT	= String.valueOf("\u00BB");


	/** The creation tool factory. */
	public static CreationEntryFactory creationToolFactory= new CreationEntryFactory();

	/** Color manager. */
	public static ColorManager colorManager= new ColorManager();

	/** Font manager. */
	public static FontManager fontManager= new FontManager();

	/** debug channel */
	protected final static int channel = 21;

	/** Factory registry. */
	private ModelerHelperRegistry factoryRegistry = null;

	/**
	 * The constructor.
	 */
	public PapyrusPlugin() {
		plugin = this;
		UML_LABEL_PROVIDER = new UMLObjectLabelProvider();
		UML_SHORT_LABEL_PROVIDER = new UMLObjectShortLabelProvider();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void start(BundleContext context) throws Exception {
		super.start(context);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void stop(BundleContext context) throws Exception {
		super.stop(context);
		plugin = null;
	}

	/**
	 * Returns the singleton instance of this plugin
	 * @return the singleton instance of this plugin
	 */
	public static PapyrusPlugin getDefault() {
		return plugin;
	}

	/**
	 * Returns an image descriptor for the image file at the given
	 * plug-in relative path.
	 * @param path the path for the image file
	 * @return the image descriptor
	 */
	public static ImageDescriptor getImageDescriptor(String path) {
		return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path);
	}

	/**
	 * Returns an <code>org.eclipse.swt.graphics.Image</code> identified by its
	 * key.<BR>
	 * By default, it returns a default image. This image is the image placed
	 * in the directory <em>resources/icons/default.gif</em>
	 * @param key the key of the image
	 * @return the Image
	 */
	public static Image getImage(String key) {
		ImageRegistry registry = getDefault().getImageRegistry();
		Image image = registry.get(key);


		if (image == null) {

			ImageDescriptor desc 
			= AbstractUIPlugin.imageDescriptorFromPlugin(
					PapyrusPlugin.PLUGIN_ID, key);

			registry.put(key, desc);
			image = registry.get(key);

		}
		if((image == null) && !key.equals(DEFAULT_IMAGE)) {
			image = getImage(DEFAULT_IMAGE);
		}

		return image;
	}

    /**
     * this method is used to obtain the icon image of the first stereotype applied to
     * this element.
     * @param elt the stereotyped element
     * @return the image of the first applied stereotype or null if there is no icon.
     */
    public static Image getIconElement(Element elt) {
        // get all stereotypes. Return icon of the first stereotype
        EList stereotypeList = elt.getAppliedStereotypes();

        if (stereotypeList.size() < 1) {
            // no applied stereotype
            return null;
        }
        return getIconForStereotype((Stereotype) stereotypeList.get(0));
    }

    /**
     * @param elt
     * @param ge
     * @return
     */
    public static Image getIconElement(Element elt, GraphElement ge) {
        // get all stereotypes. Return icon of the first stereotype
        EList stereotypeList = elt.getAppliedStereotypes();

        if (stereotypeList.size() < 1) {
            // no applied stereotype
            return null;
        }
        return getIconForStereotype((Stereotype) stereotypeList.get(0), ge);
    }

    /**
     * this method is used to obtain the icon image of the stereotype.
     * @param st the stereotype to represent
     * @return the image of the stereotype or null if there is no icon.
     */
    public static Image getIconForStereotype(Stereotype st) {
        ImageRegistry papyrusRegistry = PapyrusPlugin.getDefault().getImageRegistry();
        com.cea.papyrus.umlutils.Stereotype utilSt = new com.cea.papyrus.umlutils.Stereotype(st);
        Image icon = papyrusRegistry.get(utilSt.getIconNameForStereotype());

        // icon does not already exists
        if (icon == null) {
            icon = utilSt.getIcon();
            if (icon != null) {
                ImageData imdata = icon.getImageData();
                papyrusRegistry.put(utilSt.getIconNameForStereotype(), ImageDescriptor
                        .createFromImageData(imdata));
                icon = papyrusRegistry.get(utilSt.getIconNameForStereotype());
            }
        }
        return icon;
    }

	/**
	 * Returns the icon of the stereotype st if this latter is could be displayed, returns null otherwise.
	 * @param st Stereotype the stereotype to represent
	 * @param ge graphElement
	 * @return
	 */
	public static Image getIconForStereotype(Stereotype st, GraphElement ge) {
		Iterator<Property> iter = ge.getProperty().iterator();
		while(iter.hasNext()){
			Property currentProp = iter.next();
			if(currentProp.getKey().equals(ModelerHelper.PROPERTY_STEREOTYPE_DISPLAY)){
				if(currentProp.getValue().equals(st.getQualifiedName())) {
					//return getIconForStereotype(st);
					return getExprIconForStereotype(st, ge);
				}
			}
		}
		return null;
	}
	
	
	public static Image getExprIconForStereotype(Stereotype st, GraphElement ge){
		/* stereotyped uml element */
		Element element = ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement();
		
		/* get all icons of st */
		com.cea.papyrus.umlutils.Stereotype utilSt = new com.cea.papyrus.umlutils.Stereotype(st);
		ArrayList<org.eclipse.uml2.uml.Image> stIcons = utilSt.getAllIcons();
		
		/* if st has icons */
		if(stIcons!=null) {
			Iterator<org.eclipse.uml2.uml.Image> stIter = stIcons.iterator();
			int countIndex = 0;
			while(stIter.hasNext()) {
				org.eclipse.uml2.uml.Image currentIcon = stIter.next();
				String expression = currentIcon.getLocation();
				if(expression == null)
					return getIconForStereotype(st);
				else {
					boolean result = evalExpression(expression, st, element);
					if(result) {
						if(countIndex==0)
							return utilSt.getIcon(0);
						else
							return utilSt.getIcon(countIndex+1);
					}
					else {
						countIndex++;
					}
				}
			}
		}
	
		return null;
	}
	
	
	private static boolean evalExpression(String expression, Stereotype st, Element element){
		/*
		 * Expression has the form : propName = Literal
		 */
		
		/* Firstly we extract the propertyName and the literal */
		
		String propName=  null;
		String literal = null;

		StringTokenizer sToken = new StringTokenizer(expression.replace(" ", ""), "=");
		if(sToken.countTokens()==2) {
			propName = sToken.nextToken();
			literal = sToken.nextToken();
		}
		
		else {
			Debug.debug(null, "Bad expression in stereotype "+st.getName()+" icon", channel);
			return true;
		}
		
		if(element.hasValue(st, propName)) {
			/* extract property value from applied stereotype */
			Object val = element.getValue(st, propName);

			if(val instanceof EnumerationLiteral) {
				if(((EnumerationLiteral)val).getLabel().equals(literal)) {
					return true;
				}
			}

			else {
				Debug.debug(null, "Bad expression in stereotype "+st.getName()+" icon", channel);
				return true;
			}
		} else {
			Debug.debug(null, "Stereotype "+st.getName()+" do not have property named "+propName, channel);
		}
		return true;
	}

	
	/**
	 * this method returns the shape image that represents the first applied stereotype.
	 * 
	 * @param elt the stereotyped element
	 * 
	 * @return the image of the shape
	 */
	public static Image getShapeElement(Element elt){
		ImageRegistry papyrusRegistry=PapyrusPlugin.getDefault().getImageRegistry();
		org.eclipse.swt.graphics.Image shape =papyrusRegistry.get((new com.cea.papyrus.umlutils.Element(elt)).getShapeName());
		if (shape==null){
			shape =(new com.cea.papyrus.umlutils.Element(elt)).getShape();
			if(shape!=null){
				ImageData imdata=shape.getImageData();
				papyrusRegistry.put((new com.cea.papyrus.umlutils.Element(elt)).getShapeName(),ImageDescriptor.createFromImageData(imdata));
				shape =papyrusRegistry.get((new com.cea.papyrus.umlutils.Element(elt)).getShapeName());
			}	
		}
		return shape;
	}

	/**
	 * Returns the singleton instance of the {@link ModelerHelperRegistry} for this plugin.
	 * 
	 * @return the singleton instance of the FactoryRegistry
	 */
	public ModelerHelperRegistry getFactoryRegistry() {
		if (factoryRegistry == null) {
			factoryRegistry = new ModelerHelperExtensionRegistry(PLUGIN_ID);
		}
		return factoryRegistry;
	}

	/**
	 * Gets the papyrus editor.
	 * @return the first opened Papyrus Editor
	 */
	public static MultiDiagramEditor getPapyrusEditor(){
		//look for all editors
		for(int i=0; i <PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences().length;i++){
			IEditorReference editorRef=((IEditorReference)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences()[i]);
			IEditorPart editor=editorRef.getEditor(true);
			if(editor instanceof MultiDiagramEditor){
				return (MultiDiagramEditor) editor;
			}
		}
		return null;
	}

	/**
	 * TODO: move it in modelmanager
	 * used to load uml Metamodel
	 * @param the uml  element of the model
	 * @return the uml metamodel
	 */
	public Package getMetamodel(Element element){
		if(uml2Metamodel==null){
			uml2Metamodel = LookForElement.contentload(URI.createURI(UMLResource.UML_METAMODEL_URI), element);
		}
		return uml2Metamodel;
	}
}
